Bug 120

Bug Description:

This is a race bug for org.apache.commons.pool.impl.GenericObjectPool.
The expected exception is java.lang.IllegalStateException : Too many active instances: 3.
More details about this bug are at POOL-120 JIRA page.

Interleaving Description:

org.apache.commons.pool.impl.GenericObjectPool:

t1 t2 public void addObject() throws Exception {
assertOpen();
if (_factory == null) {
throw new IllegalStateException("Cannot add objects without a factory.");
}
1 Object obj = _factory.makeObject();
synchronized (this) {
try {
assertOpen();
4 addObjectToPool(obj, false);
} catch (IllegalStateException ex) { // Pool closed
try {
_factory.destroyObject(obj);
} catch (Exception ex2) {
// swallow
}
throw ex;
}
}
}

public Object borrowObject() throws Exception {
...
for(;;) {
ObjectTimestampPair pair = null;

synchronized (this) {
assertOpen();
// if there are any sleeping, just grab one of those
try {
3 pair = (ObjectTimestampPair)(_pool.removeFirst());
} catch(NoSuchElementException e) {
; /* ignored */
}
...
}
...

5 if(null == pair) {
try {
6 Object obj = _factory.makeObject();
...
}
...
}
...
}

public Object makeObject() {
synchronized (this) {
2 7 activeCount++;
if (activeCount > maxActive) {
8 throw new IllegalStateException(
"Too many active instances: " + activeCount);
}
}
...

Precondition: MaxActive is N, and N-1 active objects have been borrowed from pool. There are 0 idle objects in pool.
numIdle > 0 and the idle object evictor is set to run (timeBetweenEvictionRunsMillis > 0)
thread 1 is evictor thread and thread 2 is borrow thread.

a) because there are 0 idle objects in pool and minIdle is set to more than 0, thread 1 calls ensureMinIdle method to add objects into pool after evicting. So t1 executes 1, and activeCount is added to N at 2 now.
b) before thread 1 adds the object generated at step a) into pool at 4, context switched, thread 2 executes 3. And pair is set to null because pool is still empty.
c) context switched, thread 1 executes 4.
d) context switched, thread 2 checks if pair is null at 5. pair is null due to step b), so thread 2 goes to 6.
e) thread 2 executes 7, and activeCount is N+1.
f) thread 2 comes to 8, because activeCount (which is N+1) is larger than maxActicve (which is N) now.

How To Reproduce:

This bug is reproduced under pool 1.4 and JDK 1.6.0_33.
Execute the following scripts to run the test to reproduce the bug (assume the location of the pool test project is pool_test_home).

Linux:
${pool_test_home}/scripts/120.sh
Windows:
%pool_test_home%\scripts\120.bat